home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
AmigActive 2
/
AACD 2.iso
/
AACD
/
Programming
/
fpc
/
demos
/
bezier.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
1998-09-22
|
7KB
|
254 lines
Program Bezier;
{
This program draws Bezier curves using the degree elevation
method. For large numbers of points (more than 10, for
example) this is faster than the recursive way.
}
{
Changed the source to use 2.0+.
Looks a lot better.
Added CloseWindowSafely.
Made the window dynamic, it will
adjust the size after the screen size.
9 May 1998.
Translated the source to fpc.
20 Aug 1998.
nils.sjoholm@mailbox.swipnet.se
}
uses exec, intuition, graphics, utility;
{$I tagutils.inc}
type
PointRec = packed Record
X, Y : Real;
end;
Const
w : pWindow = Nil;
s : pScreen = Nil;
ltrue : longint = 1;
{
This will make the new look for screen.
SA_Pens, Integer(pens)
}
pens : array [0..0] of integer = (not 0);
Var
m : pMessage;
rp : pRastPort;
PointCount : Word;
Points : Array [1..200] of PointRec;
t, tprime : Real;
LastX, LastY : Word;
tags : array[0..13] of tTagItem;
Procedure CleanUpAndDie;
begin
if w <> Nil then CloseWindow(w);
if s <> Nil then CloseScreen(s);
if Gfxbase <> nil then CloseLibrary(GfxBase);
Halt(0);
end;
Procedure DrawLine;
begin
Move(rp, Trunc(Points[PointCount].X), Trunc(Points[PointCount].Y));
Draw(rp, LastX, LastY);
end;
Procedure GetPoints;
var
LastSeconds,
LastMicros : Longint;
IM : pIntuiMessage;
StoreMsg : tIntuiMessage;
Leave : Boolean;
OutOfBounds : Boolean;
BorderLeft, BorderRight,
BorderTop, BorderBottom : Word;
dummy : Boolean;
Procedure AddPoint;
begin
Inc(PointCount);
with Points[PointCount] do begin
X := Real(StoreMsg.MouseX);
Y := Real(StoreMsg.MouseY);
end;
with StoreMsg do begin
LastX := MouseX;
LastY := MouseY;
LastSeconds := Seconds;
LastMicros := Micros;
end;
SetAPen(rp, 2);
SetDrMd(rp, JAM1);
DrawEllipse(rp, LastX, LastY, 5, 3);
SetAPen(rp, 3);
SetDrMd(rp, COMPLEMENT);
DrawLine;
end;
Function CheckForExit : Boolean;
{ This function determines whether the user wanted to stop
entering points. I added the position tests because my
doubleclick time is too long, and I was too lazy to dig
out Preferences to change it. }
begin
with StoreMsg do
CheckForExit := DoubleClick(LastSeconds, LastMicros,
Seconds, Micros) and
(Abs(MouseX - Trunc(Points[PointCount].X)) < 5) and
(Abs(MouseY - TRunc(Points[PointCount].Y)) < 3);
end;
Procedure ClearIt;
{ This just clears the screen when you enter your first point }
begin
SetDrMd(rp, JAM1);
SetAPen(rp, 0);
RectFill(rp, BorderLeft, BorderTop,
BorderRight, BorderBottom);
SetDrMd(rp, COMPLEMENT);
SetAPen(rp, 3);
end;
begin
dummy := ModifyIDCMP(w, IDCMP_CLOSEWINDOW or IDCMP_MOUSEBUTTONS or IDCMP_MOUSEMOVE);
SetDrMd(rp, COMPLEMENT);
PointCount := 0;
Leave := False;
OutOfBounds := False;
BorderLeft := w^.BorderLeft;
BorderRight := (w^.Width - w^.BorderRight) -1;
BorderTop := w^.BorderTop;
BorderBottom := (w^.Height - w^.BorderBottom) -1;
repeat
IM := pIntuiMessage(WaitPort(w^.UserPort));
IM := pIntuiMessage(GetMsg(w^.UserPort));
StoreMsg := IM^;
ReplyMsg(pMessage(IM));
case StoreMsg.IClass of
IDCMP_MOUSEMOVE : if PointCount > 0 then begin
if not OutOfBounds then
DrawLine;
LastX := StoreMsg.MouseX;
LastY := StoreMsg.MouseY;
if (LastX > BorderLeft) and
(LastX < BorderRight) and
(LastY > BorderTop) and
(LastY < BorderBottom) then begin
DrawLine;
OutOfBounds := False;
end else
OutOfBounds := True;
end;
IDCMP_MOUSEBUTTONS : if StoreMsg.Code = SELECTUP then begin
if PointCount > 0 then
Leave := CheckForExit
else
ClearIt;
if (not Leave) and (not OutOfBounds) then
AddPoint;
end;
IDCMP_CLOSEWINDOW : CleanUpAndDie;
end;
until Leave or (PointCount > 50);
if not Leave then
DrawLine;
dummy := ModifyIDCMP(w, IDCMP_CLOSEWINDOW);
SetDrMd(rp, JAM1);
SetAPen(rp, 1);
end;
Procedure Elevate;
var
t, tprime,
RealPoints : Real;
i : Integer;
begin
Inc(PointCount);
RealPoints := Real(PointCount);
Points[PointCount] := Points[Pred(PointCount)];
for i := Pred(PointCount) downto 2 do
with Points[i] do begin
t := Real(i) / RealPoints;
tprime := 1.0 - t;
X := t * Points[Pred(i)].X + tprime * X;
Y := t * Points[Pred(i)].Y + tprime * Y;
end;
end;
Procedure DrawCurve;
var
i : Integer;
begin
Move(rp, Trunc(Points[1].X), Trunc(Points[1].Y));
for i := 2 to PointCount do
Draw(rp, Round(Points[i].X), Round(Points[i].Y));
end;
Procedure DrawBezier;
var
i : Word;
begin
SetAPen(rp, 2);
while PointCount < 100 do begin
Elevate;
DrawCurve;
if GetMsg(w^.UserPort) <> Nil then
CleanUpAndDie;
end;
SetAPen(rp, 1);
DrawCurve;
end;
begin
GfxBase := OpenLibrary(GRAPHICSNAME,37);
tags[0] := TagItem(SA_Pens, Long(@pens));
tags[1] := TagItem(SA_Depth, 2);
tags[2] := TagItem(SA_DisplayID, HIRES_KEY);
tags[3] := TagItem(SA_Title, Long(PChar('Simple Bezier Curves'#0)));
tags[4].ti_Tag := TAG_END;
s := OpenScreenTagList(nil, @tags);
if s = NIL then CleanUpAndDie;
tags[0] := TagItem(WA_IDCMP, IDCMP_CLOSEWINDOW);
tags[1] := TagItem(WA_Left, 0);
tags[2] := TagItem(WA_Top, s^.BarHeight +1);
tags[3] := TagItem(WA_Width, s^.Width);
tags[4] := TagItem(WA_Height, s^.Height - (s^.BarHeight + 1));
tags[5] := TagItem(WA_DepthGadget, ltrue);
tags[6] := TagItem(WA_DragBar, ltrue);
tags[7] := TagItem(WA_CloseGadget, ltrue);
tags[8] := TagItem(WA_ReportMouse, ltrue);
tags[9] := TagItem(WA_SmartRefresh, ltrue);
tags[10] := TagItem(WA_Activate, ltrue);
tags[11] := TagItem(WA_Title, long(PChar('Close the Window to Quit'#0)));
tags[12] := TagItem(WA_CustomScreen, long(s));
tags[13].ti_Tag := TAG_END;
w := OpenWindowTagList(nil, @tags);
IF w=NIL THEN CleanUpAndDie;
rp := w^.RPort;
Move(rp, 252, 20);
Text(rp, PChar('Enter points by pressing the left mouse button'#0), 46);
Move(rp, 252, 30);
Text(rp, PChar('Double click on the last point to begin drawing'#0), 47);
repeat
GetPoints; { Both these routines will quit if }
DrawBezier; { the window is closed. }
until False;
CleanUpAndDie;
end.